package pers.luofei.http.client.core;

import pers.luofei.http.client.annotations.*;
import pers.luofei.http.client.converter.ResponseBodyDecoder;

import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author luofei[fei12990@foxmail.com]
 * @date 2017/8/31
 */
public class ProxyMethod {

    private HttpExecutor httpExecutor;

    ProxyMethod(HttpExecutor httpExecutor) {
        this.httpExecutor = httpExecutor;
    }

    Object execute(RequestMetaInfo requestMetaInfo, Method method, Object[] args) throws Exception {

        // parse url, headers, parameters or body
        Annotation[][] originAnno = method.getParameterAnnotations();
        Class<?>[] parameterTypes = method.getParameterTypes();
        String urlStr = null;
        InputStream requestBody = null;
        Map<String, Object> requestParameters = new HashMap<>();
        Map<String, String> requestPathParameters = new HashMap<>();
        Map<String, String> customHeaders = new HashMap<>();
        ResponseCallback callBack = null;
        ResponseBodyDecoder decoder = null;
        for (int i = 0; i < originAnno.length; i++) {
            if (originAnno[i].length == 0) {
                continue;
            }
            Class<? extends Annotation> anno = originAnno[i][0].annotationType();
            if (anno == RequestHeaders.class) {
                if (Map.class.isAssignableFrom(parameterTypes[i])) {
                    Map<?, ?> tmp = (Map<?, ?>) args[i];
                    for (Object o : tmp.keySet()) {
                        if (o == null) {
                            continue;
                        }
                        Object v = tmp.get(o);
                        if (v == null) {
                            continue;
                        }
                        customHeaders.put((o.toString()), v.toString());
                    }
                } else {
                    String key = ((RequestHeaders) originAnno[i][0]).value();
                    customHeaders.put(key, args[i].toString());
                }
            } else if (anno == RequestParam.class) {
                if (Map.class.isAssignableFrom(parameterTypes[i])) {
                    Map<?, ?> tmp = (Map<?, ?>) args[i];
                    for (Object o : tmp.keySet()) {
                        if (o == null) {
                            continue;
                        }
                        Object v = tmp.get(o);
                        if (v == null) {
                            continue;
                        }
                        requestParameters.put(o.toString(), v);
                    }
                } else {
                    String key = ((RequestParam) originAnno[i][0]).value();
                    if (!key.isEmpty()) {
                        requestParameters.put(key, args[i]);
                    } else {
                        requestParameters.put(null, args[i]);
                    }
                }
            } else if (anno == RequestPath.class) {
                String key = ((RequestPath) originAnno[i][0]).value();
                if (!key.isEmpty()) {
                    requestPathParameters.put(key, String.valueOf(args[i]));
                }
            } else if (anno == RequestBody.class) {
                if (InputStream.class.isAssignableFrom(parameterTypes[i])) {
                    requestBody = (InputStream) args[i];
                }
            } else if (anno == Callback.class) {
                if (ResponseCallback.class.isAssignableFrom(parameterTypes[i])) {
                    callBack = (ResponseCallback) args[i];
                }
            }  else if (anno == ResponseDecoder.class) {
                if (ResponseBodyDecoder.class.isAssignableFrom(parameterTypes[i])) {
                    decoder = (ResponseBodyDecoder) args[i];
                }
            } else if (anno == RequestUrl.class) {
                if (String.class == parameterTypes[i]) {
                    urlStr = (String) args[i];
                    //parse url parameters
                    String[] urlAndParams = urlStr.split("\\?", 2);
                    if (urlAndParams.length == 2) {
                        urlStr = urlAndParams[0];
                        for (String kvStr : urlAndParams[1].split("&")) {
                            String[] kv = kvStr.split("=", 2);
                            if (kv.length == 1) {
                                requestParameters.put(kv[0], "");
                            } else {
                                requestParameters.put(kv[0], kv[1]);
                            }
                        }
                    }
                }
            }
        }

        // HttpConnection
        urlStr = urlStr == null ? requestMetaInfo.getUrl() : urlStr;
        for (Map.Entry<String, String> entry : requestPathParameters.entrySet()) {
            urlStr = urlStr.replace("{" + entry.getKey() + "}", entry.getValue());
        }
        HttpURLConnection connection = (HttpURLConnection) new URL(urlStr).openConnection();

        // RequestData
        RequestData requestData = new RequestData();
        requestData.setCallBack(callBack);
        requestData.setResponseBodyDecoder(decoder);
        requestData.setCustomHeaders(customHeaders);
        requestData.setRequestBody(requestBody);
        requestData.setRequestParameters(requestParameters);

        // execute
        return httpExecutor.execute(connection, requestMetaInfo, requestData);
    }
}